﻿using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Net.Sockets;
using System.Text;
using System.Threading;
using System.Threading.Tasks;

namespace dotNet
{
    public class Receiver : Context
    {
        private bool running = true;
        private Socket sck = null;
        private ReceiveCallback callback = null;          
        private TcpClient tcpclient = null;
        private Stream stream = null;

        public Thread thread = null;

        public Receiver(TcpClient tcpClient, ReceiveCallback callback)
        {
            this.stream = tcpClient.GetStream();
            this.callback = callback;
        }

        public void start()
        {
            thread = new Thread(new ThreadStart(this.run));
            thread.Start();
            running = true;
        }

        public void run()
        {
            byte[] large = new byte[65535 * 100];
            byte[] buffer = new byte[65535 * 2];
            int currSize = 0;
            int desiredSize = 0;
            int count = 0;

            Msg msg = new Msg();         

            while (running)
            {
                try
                {
                    int sz = stream.Read(buffer, 0, buffer.Length);

                    if (sz > 0) {
                        Msg.PacketInfo info = new Msg.PacketInfo();
                        info.fromByteArray(buffer);

                        if (info.magic == Msg.PacketInfo.MAGIC && count == 0) {
                            desiredSize = info.size;
                            count = info.size + Msg.PacketInfo.MAX_LENGTH;
                        } else {
                            count -= sz;
                        }

                        if (desiredSize <= currSize + sz) {
                            Buffer.BlockCopy(buffer, 0, large, currSize, sz);
                            currSize += sz;

                            while (desiredSize <= currSize) {

                                byte[] payload = new byte[desiredSize + Msg.PacketInfo.MAX_LENGTH];
                                Buffer.BlockCopy(large, 0, payload, 0, desiredSize + Msg.PacketInfo.MAX_LENGTH);
                                msg.fromByteArray(payload);
                                callback.onReceive(this, msg);    

                                if (desiredSize < currSize) {
                                    Buffer.BlockCopy(large, desiredSize + Msg.PacketInfo.MAX_LENGTH, buffer, 0, currSize - desiredSize + Msg.PacketInfo.MAX_LENGTH);
                                    Buffer.BlockCopy(buffer, 0, large, 0, currSize - desiredSize + Msg.PacketInfo.MAX_LENGTH);
                                    currSize = currSize - desiredSize - Msg.PacketInfo.MAX_LENGTH;

                                    info.fromByteArray(buffer);
                                    if (info.magic == Msg.PacketInfo.MAGIC) {
                                        desiredSize = info.size;
                                        count = info.size + Msg.PacketInfo.MAX_LENGTH;
                                    }
                                }
                            }
                            count = 0;
                        } else {
                            Buffer.BlockCopy(buffer, 0, large, currSize, sz);
                            currSize += sz;
                        }
                    } else {
                        running = false;
                    }
                } catch (SocketException se) { }
            }
        }

        public void send(Msg msg)
        {
            byte[] buffer = msg.packet.toByteArray();
            stream.Write(buffer, 0, buffer.Length);
            stream.Flush();
        }

        public void shutdown()
        {
            this.running = false;
            try {
                if (sck != null)
                    sck.Close();
                if (tcpclient != null)
                    tcpclient.Close();
            } catch (SocketException se) { }
        }
    }
}
